package org.javahispano.javacup.visores;

import java.util.Random;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import org.javahispano.javacup.modelo.PintaBalon;
import org.javahispano.javacup.modelo.PintaCancha;
import org.javahispano.javacup.modelo.PintaJugador;
import org.javahispano.javacup.modelo.PintaMarcador;
import org.javahispano.javacup.modelo.PintaPublicidad;
import org.javahispano.javacup.modelo.Transforma;
import org.javahispano.javacup.gui.principal.Principal;
import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.PartidoInterfaz;
import org.javahispano.javacup.modelo.Posicion;
import org.javahispano.javacup.modelo.ValidaTactica;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.Game;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.Sound;
import org.newdawn.slick.openal.SoundStore;
import org.newdawn.slick.opengl.InternalTextureLoader;

/** Visor OpenGL*/ 
public class VisorOpenGl implements Game, Runnable {

    private Principal principal;
    private PartidoInterfaz partido = null;
    private int sx,  sy;
    private static double escala = Constantes.ESCALA;
    private PintaCancha pc = null;
    private PintaJugador pjLocal,  pjVisita = null;
    private PintaBalon pb = null;
    private PintaMarcador pm = null;
    private PintaPublicidad pp = null;
    private double vx = 0,  vy = 0;
    private double px = 0,  py = 0;
    private int sx2,  sy2;
    private int[][] iteraciones = new int[11][2];
    private double[][] angulos = new double[11][2];
    private Posicion posPrev[][];
    private Posicion posActu[][];
    private Sound remate[] = new Sound[2],  gol,  silbato,  rebote;
    private Sound[] ambiente = new Sound[9];
    private Sound[] poste = new Sound[2];
    private Sound[] ovacion = new Sound[2];
    private Posicion balon0 = new Posicion();
    private Thread thread;
    private static boolean estadio = true;
    private static boolean entorno = true;
    private static boolean autoescala = true;
    private boolean showfps = false;
    private static boolean marcador = true;
    private static int tipoTexto = 0;
    private boolean pause = false;
    private int[] rel;
    private double dx,  dy,  z = 0,  ang = 0,  dst = 0;
    private double velgiro = 0;
    private double giro = 0;
    private int golIter = 0;
    private int iterSaca = 0;
    private int saqueIter = 0;
    private Image golImage = null,  xImage = null,  cambioImage = null;
    private GameContainer gc = null;
    private int dxsaque = 0;
    private double escalaAjustada;
    private boolean uniformeAlternativoLocal = false;
    private boolean uniformeAlternativoVisita = false;

    public VisorOpenGl(PartidoInterfaz partido, int sx, int sy, boolean fullscreen, Principal principal) throws SlickException {
        this.partido = partido;
        this.sx = sx;
        this.sy = sy;
        this.dxsaque = (sx + 300 * 2) / 75;
        sx2 = sx / 2;
        sy2 = sy / 2;
        this.principal = principal;
        AppGameContainer container = new AppGameContainer(this);
        container.setForceExit(false);
        container.setDisplayMode(sx, sy, fullscreen);
        container.start();
        SoundStore.get().clear();
        InternalTextureLoader.get().clear();
    }

    /**Inicializacion del juego*/
    @Override
    public void init(GameContainer gc) throws SlickException {
        uniformeAlternativoLocal = false;
        uniformeAlternativoLocal = ValidaTactica.usarUniformeAlternativo(partido.getDetalleLocal(), partido.getDetalleVisita());
        this.gc = gc;
        pp = new PintaPublicidad("imagenes/logos/central.png", "imagenes/logos/pubvert.png", "imagenes/logos/pubhor.png", gc.getWidth() / 2, gc.getHeight() / 2);
        golImage = new Image("imagenes/gol.png");
        cambioImage = new Image("imagenes/cambio.png");
        xImage = new Image("imagenes/x.png");
        try {
            for (int i = 0; i < ambiente.length; i++) {
                ambiente[i] = new Sound("audio/" + i + ".ogg");
            }
            remate[0] = new Sound("audio/remate1.ogg");
            remate[1] = new Sound("audio/remate2.ogg");
            poste[0] = new Sound("audio/poste1.ogg");
            poste[1] = new Sound("audio/poste2.ogg");
            ovacion[0] = new Sound("audio/ovacion1.ogg");
            ovacion[1] = new Sound("audio/ovacion2.ogg");
            gol = new Sound("audio/gol.ogg");
            rebote = new Sound("audio/rebote.ogg");
            silbato = new Sound("audio/silbato.ogg");
        } catch (Exception ex) {
            ex.printStackTrace();
        //nosound
        }
        gc.setShowFPS(false);
        pc = new PintaCancha(gc.getWidth() / 2, gc.getHeight() / 2);
        if (partido != null) {
            pjLocal = new PintaJugador();
            pjLocal.setImpl(partido.getDetalleLocal());
            pjLocal.update(uniformeAlternativoLocal);
            pjVisita = new PintaJugador();
            pjVisita.setImpl(partido.getDetalleVisita());
            pjVisita.update(uniformeAlternativoVisita);
            pb = new PintaBalon();
            posActu = partido.getPosiciones();
            posPrev = new Posicion[11][2];
            for (int i = 0; i < 11; i++) {
                iteraciones[i][0] = 0;
                iteraciones[i][1] = 0;
                posPrev[i][0] = posActu[0][i];
                posPrev[i][1] = posActu[1][i];
            }
        }
        gc.setTargetFrameRate(Constantes.FPS);
        gc.setVSync(false);
        pm = new PintaMarcador(partido.getDetalleLocal().getNombre(), partido.getDetalleVisita().getNombre());
        gc.setMouseGrabbed(false);
        thread = new Thread(this);
        thread.start();
    }
    JFileChooser jfc = new JFileChooser();
    boolean soundOk = true;

    void stop() {
        SoundStore.get().clear();
        InternalTextureLoader.get().clear();
        run = false;
        if (partido.fueGrabado()) {
            if (JOptionPane.showConfirmDialog(principal, "Desea guardar el partido?", "Guardar Partido", JOptionPane.YES_NO_OPTION) == 0) {
                if (jfc.showSaveDialog(principal) == JFileChooser.APPROVE_OPTION) {
                    try {
                        partido.getPartidoGuardado().save(jfc.getSelectedFile());
                        if (principal != null) {
                            principal.addGuardadoLocal(jfc.getSelectedFile());
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }
        if (principal != null) {
            principal.setVisible(true);
            principal.requestFocus();
        } else {
            System.exit(0);
        }
    }
    Random rand = new Random();

    float pinch() {
        return .95f + rand.nextFloat() * .1f;
    }

    /**Actualiza el juego*/
    @Override
    public void update(GameContainer gc, int fps) throws SlickException {
        Input i = gc.getInput();
        if (i.isKeyDown(Input.KEY_SUBTRACT)) {
            escala = escala * 0.970;
        } else if (i.isKeyDown(Input.KEY_ADD)) {
            escala = escala / 0.970;
        }
        if (i.isKeyPressed(Input.KEY_F1)) {
            estadio = !estadio;
        }
        if (i.isKeyPressed(Input.KEY_F2)) {
            entorno = !entorno;
        }
        if (i.isKeyPressed(Input.KEY_F3)) {
            showfps = !showfps;
            gc.setShowFPS(showfps);
        }
        if (i.isKeyPressed(Input.KEY_F4)) {
            tipoTexto = (tipoTexto + 1) % 5;
        }
        if (i.isKeyPressed(Input.KEY_F5)) {
            marcador = !marcador;
        }
        if (i.isKeyDown(Input.KEY_ESCAPE)) {
            gc.setSoundVolume(0);
            gc.setSoundOn(false);
            stop();
        }
        if (i.isKeyDown(Input.KEY_P)) {
            pause = !pause;
            if (pause) {
                gc.pause();
            } else {
                gc.resume();
            }
        }

        vx = (partido.getPosiciones()[2][0].getX() - px) / Constantes.SEGUIMIENTO_CAMARA;
        vy = (partido.getPosiciones()[2][0].getY() - py) / Constantes.SEGUIMIENTO_CAMARA;

        px = px + vx;
        py = py + vy;

        for (int j = 0; j < 11; j++) {
            posPrev[j][0] = posActu[0][j];
            posPrev[j][1] = posActu[1][j];
        }

        if (partido != null) {
            try {
                partido.iterar();
            } catch (Exception ex) {
                ex.printStackTrace();
                throw new SlickException(ex.getLocalizedMessage());
            }
            if (partido.estanSilbando()) {
                if (soundOk) {
                    try {

                        silbato.play(pinch(), volumenCancha);
                    } catch (Exception e) {
                        soundOk = false;
                        e.printStackTrace();
                    }
                }
            }
            if (partido.estanRematando()) {
                remate[rand.nextInt(2)].play(pinch(), volumenCancha);
            }
            if (partido.estanOvacionando()) {
                try {
                    ovacion[rand.nextInt(2)].play(pinch(), volumenAmbiente);
                } catch (Exception e) {
                    soundOk = false;
                    e.printStackTrace();
                }
            }
            if (partido.esGol()) {
                try {
                    gol.play(pinch(), volumenAmbiente);
                } catch (Exception e) {
                    soundOk = false;
                    e.printStackTrace();
                }
                golIter = 1;
            }

            if (golIter > 0) {
                golIter++;
                if (golIter == 50) {
                    golIter = 0;
                }
            }
            if (partido.cambioDeSaque()) {
                try {
                    silbato.play(pinch(), volumenAmbiente);
                } catch (Exception e) {
                    soundOk = false;
                    e.printStackTrace();
                }
                saqueIter = 1;
            }

            if (saqueIter > 0) {
                saqueIter = saqueIter + dxsaque;
                if (saqueIter > sx + 2 * 177) {
                    saqueIter = 0;
                }
            }
            if (partido.estaRebotando()) {
                try {
                    rebote.play(pinch(), volumenCancha);
                } catch (Exception e) {
                    soundOk = false;
                    e.printStackTrace();
                }
            }
            if (partido.esPoste()) {
                try {
                    poste[rand.nextInt(2)].play(pinch(), volumenCancha);
                    ovacion[rand.nextInt(2)].play(pinch(), volumenAmbiente);
                } catch (Exception e) {
                    soundOk = false;
                    e.printStackTrace();
                }
            }
            if (partido.estanSacando()) {
                iterSaca = (iterSaca + 1) % 6;
            }
        }

        posActu = partido.getPosiciones();
        for (int j = 0; j < 11; j++) {
            dx = posPrev[j][0].getX() - posActu[0][j].getX();
            dy = posPrev[j][0].getY() - posActu[0][j].getY();
            if (dy != 0 || dx != 0) {
                iteraciones[j][0] = iteraciones[j][0] + 1;
                angulos[j][0] = posPrev[j][0].angulo(posActu[0][j]) * 180 / Math.PI + 90;
            } else {
                iteraciones[j][0] = 3;
            }

            dx = posPrev[j][1].getX() - posActu[1][j].getX();
            dy = posPrev[j][1].getY() - posActu[1][j].getY();
            if (dy != 0 || dx != 0) {
                iteraciones[j][1] = iteraciones[j][1] + 1;
                angulos[j][1] = posPrev[j][1].angulo(posActu[1][j]) * 180 / Math.PI + 90;
            } else {
                iteraciones[j][1] = 3;
            }

        }
        if (autoescala) {
            int[] escalas = (Transforma.transform(partido.getPosVisibleBalon(), Constantes.centroCampoJuego, -Transforma.transform(px, escala), -Transforma.transform(py, escala), escala));
            escalaAjustada = escala * Math.min(0.7d * sx2 / (double) Math.abs(escalas[0]), 0.7d * sy2 / (double) Math.abs(escalas[1]));
        }
        if (!noAutoEscalar && partido.esGol()) {
            noAutoEscalar = true;
        }
        if (noAutoEscalar && partido.estanRematando()) {
            noAutoEscalar = false;
        }
    }
    double escalaGradual = 0;
    boolean noAutoEscalar = false;

    /**Renderiza el juego*/
    @Override
    public void render(GameContainer gc, Graphics g) throws SlickException {
        double escalaTemporal = 0;
        if (autoescala) {
            escalaTemporal = escala;
            if (!noAutoEscalar) {
                escala = Math.min(escala, escalaAjustada);
                escalaGradual = escalaGradual + (escala - escalaGradual) * 0.2;
            }

            escala = escalaGradual;
        }
        Posicion p = new Posicion(px, py);
        if (entorno) {
            pc.pintaEntorno(g, p, escala);
        }
        pc.pintaCancha(g, p, escala);
        Posicion[][] pos = partido.getPosiciones();
        if (!partido.esGol() && partido.estanSacando()) {
            double zoom = 1 * escala * (1 + 0.02 * (double) iterSaca);
            rel = Transforma.transform(pos[2][0], Constantes.centroCampoJuego, -Transforma.transform(px, escala) + sx2, -Transforma.transform(py, escala) + sy2, escala);
            g.drawImage(xImage.getScaledCopy((int) zoom, (int) zoom), rel[0] - (int) (zoom / 2), rel[1] - (int) (zoom / 2));

        }
        Posicion ball = partido.getPosVisibleBalon();
        for (int i = 0; i < 11; i++) {
            rel = Transforma.transform(pos[0][i], Constantes.centroCampoJuego, -Transforma.transform(px, escala) + sx2, -Transforma.transform(py, escala) + sy2, escala);
            pjLocal.pintaSombra(i, iteraciones[i][0], angulos[i][0], escala, rel[0], rel[1], g);
            rel = Transforma.transform(pos[1][i], Constantes.centroCampoJuego, -Transforma.transform(px, escala) + sx2, -Transforma.transform(py, escala) + sy2, escala);
            pjVisita.pintaSombra(i, iteraciones[i][1], angulos[i][1], escala, rel[0], rel[1], g);
        }
        z = partido.getAlturaBalon();// 16*Math.sin(Math.abs(ang % Math.PI));

        if (partido.estaRebotando() || z == 0) {
            ang = -balon0.angulo(ball);
            velgiro = balon0.distancia(ball) * 1.5;
        }
        if (partido.estanRematando()) {
            ang = rand.nextDouble() * Math.PI * 2;
            velgiro = rand.nextDouble();
        }

        balon0 = ball;
        giro = giro + velgiro * 1.5;
        if (giro < 0) {
            giro = 6 + giro;
        }
        rel = Transforma.transform(ball, Constantes.centroCampoJuego, -Transforma.transform(px, escala) + sx2, -Transforma.transform(py, escala) + sy2, escala);
        pb.pintaSombra(escala, rel[0], rel[1], z, g);
        if (partido.getAlturaBalon() <= 2) {
            pb.pintaBalon((int) (giro), ang, escala, rel[0], rel[1], z * 2, g);
        }
        pp.pintaPublicidad(g, p, escala);
        for (int i = 0; i < 11; i++) {
            rel = Transforma.transform(pos[0][i], Constantes.centroCampoJuego, -Transforma.transform(px, escala) + sx2, -Transforma.transform(py, escala) + sy2, escala);
            pjLocal.pintaJugador(i, iteraciones[i][0], angulos[i][0], escala, rel[0], rel[1], g);
            if (tipoTexto == 3 || (pos[0][i].distancia(pos[2][0]) < 8 && tipoTexto == 1)) {
                pjLocal.pintaNumero(i, rel[0], rel[1], g);
            }
            if (tipoTexto == 4 || (pos[0][i].distancia(pos[2][0]) < 8 && tipoTexto == 2)) {
                pjLocal.pintaNombre(i, rel[0], rel[1], g);
            }
            rel = Transforma.transform(pos[1][i], Constantes.centroCampoJuego, -Transforma.transform(px, escala) + sx2, -Transforma.transform(py, escala) + sy2, escala);
            pjVisita.pintaJugador(i, iteraciones[i][1], angulos[i][1], escala, rel[0], rel[1], g);
            if (tipoTexto == 3 || (pos[1][i].distancia(pos[2][0]) < 8 && tipoTexto == 1)) {
                pjVisita.pintaNumero(i, rel[0], rel[1], g);
            }
            if (tipoTexto == 4 || (pos[1][i].distancia(pos[2][0]) < 8 && tipoTexto == 2)) {
                pjVisita.pintaNombre(i, rel[0], rel[1], g);
            }

        }

        rel = Transforma.transform(ball, Constantes.centroCampoJuego, -Transforma.transform(px, escala) + sx2, -Transforma.transform(py, escala) + sy2, escala);
        if (partido.getAlturaBalon() > 2) {
            pb.pintaBalon((int) (giro), ang, escala, rel[0], rel[1], z * 2, g);
        }
        pc.pintaArcos(g, p, escala);
        if (estadio) {
            pc.pintaEstadio(g, p, escala);
        }
        if (marcador) {
            pm.pintaMarcador(partido.getGolesLocal(), partido.getGolesVisita(), partido.getIteracion(), partido.getPosecionBalonLocal(), g);
        }
        if (golIter > 0) {
            double zoom = 1 + 0.05 * (golIter % 3);
            g.drawImage(golImage.getScaledCopy((int) (361d * zoom), (int) (81d * zoom)), sx2 - (int) (180d * zoom), sy2 - (int) (40d * zoom));
        }
        if (saqueIter > 0) {
            g.drawImage(cambioImage, saqueIter - 300, sy2 - 20);
        }
        if (autoescala) {
            escala = escalaTemporal;
        }
    }

    /**metodo llamado cuando se cierra la ventana*/
    @Override
    public boolean closeRequested() {
        stop();
        return true;
    }

    /**Metodo llamado para obtener el titulo de la ventana*/
    @Override
    public String getTitle() {
        return "JavaCup 2009";
    }
    private boolean run = true;
    private static float volumenAmbiente = 1;

    /**Setea el volumen ambiente*/
    public static void setVolumenAmbiente(float volumen) {
        volumenAmbiente = volumen;
    }
    private static float volumenCancha = 1;

    /**Setea el volumen dentro del campo de juego*/
    public static void setVolumenCancha(float volumen) {
        volumenCancha = volumen;
    }

    /**Setea la escala*/
    public static void setEscala(double escala) {
        VisorOpenGl.escala = escala;
    }

    /**Setea la modalida auto-escala*/
    public static void setAutoescala(boolean auto) {
        VisorOpenGl.autoescala = auto;
    }

    /**Setea si el marcador esta visible*/
    public static void setMarcadorVisible(boolean visible) {
        marcador = visible;
    }

    /**Setea se el entorno externo al estadio es visible*/
    public static void setEntornoVisible(boolean visible) {
        entorno = visible;
    }

    /**Setea si el estadio es visible*/
    public static void setEstadioVisible(boolean visible) {
        estadio = visible;
    }

    /**Cambia la modalidad del texto de los nombres y numeros de los jugadores*/
    public static void setTexto(int tipo) {
        tipoTexto = tipo;
    }

    /**Ejecuta en un thread los sonidos ambientes*/
    @Override
    public void run() {
        int idx = 1;
        while (run) {
            try {
                try {
                    ambiente[idx].play(pinch(), volumenAmbiente);
                } catch (Exception e) {
                    soundOk = false;
                    e.printStackTrace();
                }
                Thread.sleep(2500 + rand.nextInt(1000));
            } catch (Exception ex) {
                run = false;
            }
            idx = rand.nextInt(ambiente.length);
        }
        gc.exit();
        if (principal != null) {
            principal.setVisible(true);
        }
    }
}
